import 'package:homepad/iot/basic_types.dart';
import 'package:homepad/iot/message_def.dart';
import 'package:homepad/logger/logger.dart';
import 'light_dimmer.dart';

class FavoriteColor {
  final ColorMode mode;
  final int colorTemperature;
  final int hue;
  final int saturation;

  const FavoriteColor.colorTemperature({
    required this.colorTemperature,
  })  : mode = ColorMode.colorTemperature,
        hue = 0,
        saturation = 0;

  const FavoriteColor.hueSaturation({
    required this.hue,
    required this.saturation,
  })  : mode = ColorMode.hueAndSaturation,
        colorTemperature = 2700;
}

const _defaultColors = [
  FavoriteColor.colorTemperature(colorTemperature: 2700),
  FavoriteColor.colorTemperature(colorTemperature: 6500),
  FavoriteColor.hueSaturation(hue: 60, saturation: 100),
  FavoriteColor.hueSaturation(hue: 120, saturation: 100),
  FavoriteColor.hueSaturation(hue: 180, saturation: 100),
  FavoriteColor.hueSaturation(hue: 240, saturation: 100),
];

const _defaultColorTemperatures = [
  FavoriteColor.colorTemperature(colorTemperature: 2700),
  FavoriteColor.colorTemperature(colorTemperature: 3000),
  FavoriteColor.colorTemperature(colorTemperature: 3500),
  FavoriteColor.colorTemperature(colorTemperature: 4000),
  FavoriteColor.colorTemperature(colorTemperature: 5000),
  FavoriteColor.colorTemperature(colorTemperature: 6500),
];

class LightColor extends LightDimmer {
  final List<ColorCapability> _capabilities = [];
  LightColor.fromJson(super.json): super.fromJson() {
    final array = extra?["capabilities"] as List<dynamic>?;
    if (array == null) {
      return;
    }
    for (var item in array) {
      final capability = ColorCapability.byValue(item);
      if (capability != null) {
        _capabilities.add(capability);
      }
    }
  }

  List<FavoriteColor> get favorites =>
      isSupported(ColorCapability.hueSaturation) ? _defaultColors : _defaultColorTemperatures;
  bool isSupported(ColorCapability ability) =>_capabilities.contains(ability);

  var ct = 2700;
  var hue = 240;
  var saturation = 60;
  var mode = ColorMode.colorTemperature;

  void setColorTemperature(int value) {
    ct = value;
    delegate.onAttributeSet(AttributePathIB(
        endpoint: endpointId,
        cluster: ClusterIds.colorControl,
        attribute: ColorControlAttributeIds.colorTemperatureMireds),
        1000000 ~/ ct);
    delegate.onStateChanged("ep:$endpointId-ct:$ct");

    if (mode != ColorMode.colorTemperature) {
      mode = ColorMode.colorTemperature;
      delegate.onStateChanged("ep:$endpointId-ct:$value");
    }
  }

  void setColorMode(ColorMode mode) {
    this.mode = mode;
    delegate.onStateChanged("ep:$endpointId-mode:$mode");
    switch(mode) {
      case ColorMode.hueAndSaturation:
        setHue(hue, saturation: saturation);
      case ColorMode.colorTemperature:
        setColorTemperature(ct);
    }
  }

  void setFavoriteColor(FavoriteColor color) {
    switch (color.mode) {
      case ColorMode.colorTemperature:
        setColorTemperature(color.colorTemperature);
      case ColorMode.hueAndSaturation:
        setHue(color.hue, saturation: color.saturation);
    }
  }

  void setHue(int value, {int? saturation}) {
    hue = value;
    if (saturation != null) {
      this.saturation = saturation;
    }
    delegate.onAttributeSet(AttributePathIB(
        endpoint: endpointId,
        cluster: ClusterIds.colorControl,
        attribute: ColorControlAttributeIds.currentHue),
        (value.toDouble() * 255 / 360).toInt());
    delegate.onAttributeSet(AttributePathIB(
        endpoint: endpointId,
        cluster: ClusterIds.colorControl,
        attribute: ColorControlAttributeIds.currentSaturation),
        (this.saturation.toDouble() * 255 / 100).toInt());

    if (saturation != null) {
      mode = ColorMode.hueAndSaturation;
      delegate.onStateChanged("ep:$endpointId-hs:$hue,$saturation");
    }
  }

  void setSaturation(int value) {
    saturation = value;
    delegate.onAttributeSet(AttributePathIB(
        endpoint: endpointId,
        cluster: ClusterIds.colorControl,
        attribute: ColorControlAttributeIds.currentSaturation),
        (value.toDouble() * 255 / 100).toInt());
  }

  @override
  void readAllState() {
    super.readAllState();
    super.readAttribute(ClusterIds.colorControl, ColorControlAttributeIds.colorMode);
    super.readAttribute(ClusterIds.colorControl, ColorControlAttributeIds.colorTemperatureMireds);
    super.readAttribute(ClusterIds.colorControl, ColorControlAttributeIds.currentHue);
    super.readAttribute(ClusterIds.colorControl, ColorControlAttributeIds.currentSaturation);
  }

  @override
  bool onAttributeReport(AttributePathIB path, data) {
    if (path.cluster != ClusterIds.colorControl) {
      return super.onAttributeReport(path, data);
    }

    if (path.attribute == ColorControlAttributeIds.colorMode) {
      final newValue = ColorMode.byValue(data as int);
      if (newValue == null || newValue == mode) {
        return false;
      }
      mode = newValue;
      return true;
    } else if (path.attribute == ColorControlAttributeIds.colorTemperatureMireds) {
      final newValue = 1000000 ~/ (data as int);
      if (newValue == ct) {
        return false;
      }
      ct = ((newValue + 25.0) / 50).toInt() * 50;;
      return true;
    } else if (path.attribute == ColorControlAttributeIds.currentHue) {
      final newValue = ((data as int) * 360 / 255 + 0.5).toInt();
      if ((newValue - hue).abs() < 3) {
        return false;
      }
      logger.trace("hue: $hue");
      hue = newValue;
      return true;
    } else if (path.attribute == ColorControlAttributeIds.currentSaturation) {
      final newValue = ((data as int) * 100 / 255 + 0.5).toInt();
      if ((newValue - saturation).abs() < 2) {
        return false;
      }
      logger.trace("saturation: $saturation");
      saturation = newValue;
      return true;
    }
    return false;
  }
}